import { useState, useCallback, FC, useEffect, ReactElement } from 'react';
import { Upload, UploadProps, Button, Input, message } from 'antd';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
import { useField } from '@formily/react';
import { UploadFile, UploadChangeParam } from 'antd/lib/upload/interface';

import './style/index.less';
import UploadListItem from './UploadListItem';
import { saveAs } from 'file-saver';

import { FormattedMessage } from 'umi';
import { uuid } from '@ey-utils/utils';
import { getToken } from "@ey-utils/utils/local-storage";

import Resumable from 'resumablejs';
import { ApiConfig, DynamicObjectAny } from '@/typings';
import { toArray } from '@/biz-components/_common/valueUtil';
import FilePreviewModal from '@/biz-components/slice-file-upload/modal';
import { useVisible } from '@/biz-components/modals/hooks';

import { UploadOutlined } from '@ant-design/icons';
import { useIntl } from 'umi';
import { UpSvgIcon } from '@/components/SelfIcon';
import { IMG_TYPE } from './modal/view-container';
import { ImgPreview } from './modal/img-preview';

const { Search } = Input;

const { Dragger } = Upload;

export interface UploadFileItem extends UploadFile {
  id?: string;
}

export type UploadFiles = UploadFileItem[];

export interface FileUploadProps
  extends Omit<UploadProps, 'fileList' | 'onChange' | 'beforeUpload' | 'itemRender'> {
  api?: ApiConfig;
  onChange?: (fileList: any) => void;
  editable?: boolean;
  value?: UploadFiles;
  mode?: 'dragger' | 'button' | 'input' | 'icon';
  pageId?: string;
  operationAuth?: string[];
  eventMap?: {
    fileRemoveCb?: (id: string) => void;
    [key: string]: any
  };
}

const PREVIEW_STATUS = ['success', 'done'];

const SliceFileUpload: FC<FileUploadProps> = ({
  api,
  onChange,
  value,
  mode,
  editable,
  pageId: propsPageId,
  eventMap,
  operationAuth,
  ...restProps
}) => {
  const [fileList, setFileList] = useState<UploadFiles>([]);
  const { formatMessage } = useIntl();
  const [visible, showModal, hiddenModal] = useVisible(false);

  const [dataSource, setDataSource] = useState<any>([]);
  const [index, setIndex] = useState(-1);
  //TODO: 获取值
  const field = useField();

  const [stateCb, setStateCb] = useState(false);

  const [visiblePreview , showPreview, hiddenPreview] = useVisible(false);

  const moveRow = useCallback(
    (dragIndex, hoverIndex) => {
      const dragRow = fileList[dragIndex];
      setFileList(
        update(fileList, {
          $splice: [
            [dragIndex, 1],
            [hoverIndex, 0, dragRow],
          ],
        }),
      );

      setStateCb(true);
    },
    [fileList],
  );

  const changeFileStatus = (
    uid: string,
    status: UploadFile['status'],
    extraItem?: DynamicObjectAny,
  ) => {
    setFileList((list) => {
      return list.map((item) => {
        if (item.uid === uid) {
          return {
            ...item,
            status,
            ...extraItem,
          };
        }
        return item;
      });
    });

    if (status === 'done' || status === 'success') {
      setStateCb(true);
    }
  };

  const uploadFile = (originFileObj: UploadFile) => {
    const { type, method, url, params } = api || {};

    if (!(url && (type || method))) return;

    const { fileName, name, size, uid } = originFileObj;
    const Authorization = getToken();

    const pageId = field?.query('pageId')?.getIn()?.value || propsPageId || '';

    if (!pageId) {
      message.warn(`缺少配置 pageId 无法上传附件 `);
      return;
    }

    const resumable = new Resumable({
      target: url, // 后端接口地址
      query: {
        ...params,
        pageId,
      },
      chunkSize: 512 * 1024, //
      maxChunkRetries: 2,
      simultaneousUploads: 3,
      testChunks: false,
      // @ts-ignore
      throttleProgressCallbacks: 1,
      method: 'octet',
      uploadMethod: ((type || method)?.toLocaleUpperCase() as ApiConfig['method']) || 'POST',
      permanentErrors: [400, 404, 415, 500, 501, 401],
      generateUniqueIdentifier: (): string => {
        const relativePath = fileName || name;
        return `${size}-${relativePath.replace(/[^0-9a-zA-Z_-]/gim, '')}-${uuid()}`;
      },
      headers: {
        Authorization,
      },
    });

    const file = (originFileObj as unknown) as File;

    resumable.addFile(file);

    resumable.on('fileAdded', () => {
      resumable.upload();

      changeFileStatus(uid, 'uploading');
    });

    resumable.on('fileProgress', () => {
      setFileList((list) => {
        return list.map((item) => {
          if (item.uid === uid) {
            return {
              ...item,
              percent: resumable.progress() * 100,
            };
          }
          return item;
        });
      });
    });

    resumable.on('fileSuccess', (fileEvent: any, res: any) => {
      const { result, success } = JSON.parse(res || '{}');
      changeFileStatus(uid, success ? 'done' : 'error', { uid: result, id: result });
    });

    resumable.on('fileError', () => {
      changeFileStatus(uid, 'error');
    });
  };

  const triggerChange = (changedValue: UploadFiles) => {
    onChange?.(
      changedValue
        ?.map((item) => {
          const { id, name, status } = item;
          return status === 'done' || status === 'success'
            ? {
                id,
                name,
              }
            : '';
        })
        .filter((val) => val) || [],
    );
  };

  const handleChange = (info: UploadChangeParam) => {
    const { file: originFileObj, fileList: newFileList } = info;

    if (!originFileObj.status) {
      uploadFile(originFileObj);
      setFileList(newFileList);
    }
  };

  const beforeUpload = () => {
    return false;
  };

  useEffect(() => {
    setFileList((prevList) => {
      const propsValue = toArray<UploadFile>(value);

      if (propsValue.length) {
        return prevList
          .filter((item) => {
            return !['done', 'success'].includes(item?.status || '');
          })
          .concat(
            propsValue.map((item) => {
              // @ts-ignore
              const { uid, id, status } = item;
              return {
                ...item,
                uid: uid || id,
                status: id ? 'done' : status,
              };
            }),
          );
      }

      return prevList.filter((item) => !['done', 'success'].includes(item?.status || ''));
    });
  }, [value]);

  useEffect(() => {
    if (stateCb) {
      triggerChange(fileList);
      setStateCb(false);
    }
  }, [fileList, stateCb]);

  const handleRemove = (file: UploadFile) => {
    setFileList((list) => {
      return list.filter((item) => item.uid !== file.uid);
    });

    setStateCb(true);

    const { fileRemoveCb } = eventMap || {};

    fileRemoveCb?.(file.id);
  };

  const getFileUrl = (code: string) => {
    return `/afm/attachment/download?code=${code}&optimize=false`;
  };

  const findIndex = (data: any[], uid: string) => {
    return (
      data?.findIndex((item) => {
        return item.uid === uid;
      }) ?? -1
    );
  };

  const getPreviewData = (type: 'img' | 'file') => {
    const previewData: Array<{ uid: string, fileUrl: string, type: string }> = [];

    fileList.filter((item: UploadFileItem) => {
      return PREVIEW_STATUS.includes(item.status || '');
    }).map((item) => {
      const filenameExtension: string = item.name?.split('.').pop() || '';
      const isImg = IMG_TYPE.includes(`.${filenameExtension}`);

      if ((type === 'img' && isImg) || (type === 'file' && !isImg)) {
        previewData.push({
          uid: item.uid,
          fileUrl: getFileUrl(item.uid),
          type: `.${filenameExtension}`
        })
      }
    })

    return previewData;
  }

  const handlePreview = (file: UploadFile) => {
    const filenameExtension: string = file.name?.split('.').pop() || '';
    const isImg = IMG_TYPE.includes(`.${filenameExtension}`);
    const previewData = getPreviewData(isImg ? 'img' : 'file');

    setIndex(findIndex(previewData, file.uid));
    setDataSource(previewData);

    if (isImg) {
      showPreview();
      return;
    }

    showModal();
  };

  const handleNewPagePreview = (file: UploadFile) => {
    const previewData = getPreviewData('file');
    const url = `/#/file-preview?dataSource=${encodeURIComponent(JSON.stringify(previewData))}&index=${findIndex(previewData, file.uid)}`

    window.open(url);
  }

  const handleDownload = (file: UploadFile) => {
    saveAs(`/afm/attachment/download?code=${file.uid}&optimize=false`);
  };

  const itemRender = (_: ReactElement, file: UploadFile, currFileList?: UploadFile[]) => {
    return (
      <UploadListItem
        file={file}
        index={findIndex(fileList, file.uid)}
        operationAuth={operationAuth}
        moveRow={moveRow}
        onRemove={handleRemove}
        onPreview={handlePreview}
        onNewPagePreview={handleNewPagePreview}
        onDownload={handleDownload}
      />
    );
  };

  const props = {
    ...restProps,
    fileList,
    onChange: handleChange,
    beforeUpload,
    itemRender,
  };

  const renderUploadMode = () => {
    if (!editable) {
      return fileList?.map((item) => {
        return (
          <UploadListItem
            file={item}
            index={findIndex(fileList, item.uid)}
            key={item.uid}
            operationAuth={operationAuth}
            moveRow={moveRow}
            onRemove={handleRemove}
            onPreview={handlePreview}
            onNewPagePreview={handleNewPagePreview}
            onDownload={handleDownload}
            showRemoveIcon={false}
          />
        );
      });
    }

    switch (mode) {
      case 'input': {
        return (
          <Upload {...props} multiple>
            <Search
              placeholder={formatMessage({
                id: 'upload.mode.input.placeholder',
              })}
              readOnly
              enterButton={formatMessage({ id: 'upload.mode.input.button' })}
            />
          </Upload>
        );
      }

      case 'button': {
        return (
          <Upload {...props} multiple>
            <Button icon={<UploadOutlined />}>
              {formatMessage({
                id: 'upload.mode.button',
              })}
            </Button>
          </Upload>
        );
      }

      case 'icon': {
        return (
          <Upload {...props} multiple>
            <UploadOutlined />
          </Upload>
        );
      }

      case 'dragger':
      default: {
        return (
          <Dragger {...props} multiple>
            <p className="ant-upload-drag-icon" style={{ marginBottom: 5, position: 'relative' }}>
              {/* <div className="up-content"></div> */}
              <UpSvgIcon />
            </p>
            <p className="ant-upload-text">
              <FormattedMessage id="click.ro.drag.to.upload" />
            </p>
          </Dragger>
        );
      }
    }
  };

  return (
    <>
      <DndProvider backend={HTML5Backend}>{renderUploadMode()}</DndProvider>

      <FilePreviewModal
        needReset
        visible={visible}
        dataSource={dataSource}
        onCancel={hiddenModal}
        index={index}
      />
      <ImgPreview
        visible={visiblePreview}
        previewIndex={index}
        previewData={dataSource}
        onClose={hiddenPreview}
      />
    </>
  );
};

SliceFileUpload.defaultProps = {
  mode: 'dragger',
  editable: true,
};

export default SliceFileUpload;
